package org.seeplnframework.skyeye.common.util;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.seeplnframework.skyeye.common.trace.TraceContext;
import org.seeplnframework.skyeye.common.constants.SysConstant;
import org.seeplnframework.skyeye.common.enums.LogLevelEnum;
import org.seeplnframework.skyeye.common.enums.MDCConstantEnum;
import org.seeplnframework.skyeye.common.model.FilterMessage;
import org.seeplnframework.skyeye.common.model.LogData;

import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class LogUtils {

    /**
     * 异常过滤
     *
     * @param filterMessage
     * @return
     */
    public static boolean filter(FilterMessage filterMessage) {
        String msg = filterMessage.getMsg();
        String message = filterMessage.getMessage();
        String stackTrace = filterMessage.getStackTrace();
        // 获取配置
        String alarmWhiteList = ConfigUtils.getProperty(SysConstant.ALARM_WHITE_LIST, null);

        return filter(msg, alarmWhiteList) || filter(message, alarmWhiteList) || filter(stackTrace, alarmWhiteList);
    }

    /**
     * 获取LogData数据对象
     *
     * @param logType
     * @param msg
     * @param message
     * @param stackTrace
     * @return
     */
    public static LogData obtainLogData(String logType, String msg, String message, String stackTrace) {
        // 获取配置
        String stackNumStr = ConfigUtils.getProperty(SysConstant.ALARM_STACKNUM, null);
        int stackNum = StringUtils.isNotBlank(stackNumStr) ? Integer.parseInt(stackNumStr.trim()) : SysConstant.ALARM_STACKNUM_DEFAULT;
        // logData
        LogData logData = new LogData();
        logData.setLevel(getLogLevel(msg));
        logData.setErrorMessage(msg);
        logData.setServerName(ConfigUtils.getApplicationName());
        logData.setSystemDesc(ConfigUtils.getSystemDesc());
        String ip = Optional
                .ofNullable(K8sUtils.getSelfIp())
                .filter(it -> StringUtils.isNotBlank(it) && !"0.0.0.0".equalsIgnoreCase(it))
                .orElseGet(() -> IPUtils.getLocalIp());
        logData.setServerIP(ip);
        logData.setServerHostname(IPUtils.getLocalHostName());
        logData.setSourceIP(MDCUtils.get(MDCConstantEnum.SOURCE_IP.getCode()));
        logData.setRequestType(MDCUtils.get(MDCConstantEnum.REQUEST_TYPE.getCode()));
        logData.setTraceId(TraceContext.traceId());
        logData.setRequestURI(MDCUtils.get(MDCConstantEnum.REQUEST_URI.getCode()));
        logData.setRequestParam(MDCUtils.get(MDCConstantEnum.REQUEST_PARAM.getCode()));
        logData.setRequestHeader(MDCUtils.get(MDCConstantEnum.REQUEST_HEADER.getCode()));
        logData.setMqType(MDCUtils.get(MDCConstantEnum.MQ_TYPE.getCode()));
        logData.setConsumerGroup(MDCUtils.get(MDCConstantEnum.CONSUMER_GROUP.getCode()));
        logData.setMessageTopic(MDCUtils.get(MDCConstantEnum.MESSAGE_TOPIC.getCode()));
        logData.setMessageId(MDCUtils.get(MDCConstantEnum.MESSAGE_ID.getCode()));
        logData.setMessageKeys(MDCUtils.get(MDCConstantEnum.MESSAGE_KEYS.getCode()));
        logData.setEventName(MDCUtils.get(MDCConstantEnum.EVENT_NAME.getCode()));
        logData.setEventPayload(MDCUtils.get(MDCConstantEnum.EVENT_PAYLOAD.getCode()));
        logData.setExceptionMessage(message);
        String exceptionStackTrace = getRegexContent(stackTrace, stackNum);
        if (!exceptionStackTrace.contains("Caused by")) {
            String causedByContent = getCausedByContentOfStackTrace(stackTrace, 1);
            exceptionStackTrace += causedByContent;
        }
        exceptionStackTrace = StrUtil.subBefore(exceptionStackTrace, System.getProperty("line.separator"), true);
        logData.setExceptionStackTrace(exceptionStackTrace);
        return logData;
    }

    /**
     * 信息过滤
     *
     * @param msg
     * @param filter
     * @return
     */
    public static boolean filter(String msg, String filter) {
        if (StringUtils.isNotBlank(msg) && StringUtils.isNotBlank(filter)) {
            String[] filters = filter.split(",");
            for (String filt : filters) {
                if (msg.contains(filt)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 日志级别
     *
     * @param msg
     * @return
     */
    private static String getLogLevel(String msg) {
        String level = LogLevelEnum.NORMAL.name();
        if (StringUtils.isNotBlank(msg)) {
            return msg.toLowerCase().contains("level") && msg.toLowerCase().contains("serious") ? LogLevelEnum.SERIOUS.name() : LogLevelEnum.NORMAL.name();
        }

        return level;
    }

    /**
     * 字符串正则截取
     *
     * @param content
     * @param seq
     * @return
     */
    private static String getRegexContent(String content, int seq) {
        Matcher slashMatcher = Pattern.compile(System.getProperty("line.separator")).matcher(content);
        int mIdx = 0;
        do {
            if (!slashMatcher.find()) {
                return content;
            }
            ++mIdx;
        } while(mIdx != seq);

        return content.substring(0, slashMatcher.start());
    }


    /**
     * 截取异常堆栈的Caused by内容
     *
     * @param stackTrace
     * @param stackNum
     * @return
     */
    private static String getCausedByContentOfStackTrace(String stackTrace, int stackNum) {
        String content = "";
        if (stackTrace.contains("Caused by")) {
            content = System.getProperty("line.separator") + getRegexContent(stackTrace.substring(stackTrace.lastIndexOf("Caused by")), stackNum);
        }

        return content;
    }

}
